using System;
using Qyoto;
using System.Drawing; 
using Clandestine.Engines.Field;

namespace Shlick
{
	public class TilemapView : QGraphicsView
	{
		public MapDocument MapDocument { get; set; }
		public Map Map { get { return this.MapDocument.Map; } }
		public int GridSize { get { return Map.GridSize; } }
		private Spritesheet Spritesheet;
		
		// UI Stuff
		private QGraphicsScene scene;
		private QGraphicsPixmapItem tilemap;
		private QGraphicsRectItem dragRect; // the rectangle used to show the selection
		private QGraphicsRectItem hoverRect; // the rect when hovering over a single tile
		private Point hoverPosition; // easy access in mouseRelease event -- tile co-ords
		private QPoint mouseDown;
				
		public TilemapView(MapDocument mapDocument) : base()
		{
			this.MapDocument = mapDocument;

			this.Reset();	
		}
		
		public void Reset()
		{
			// Setup Scene/View
			scene = new QGraphicsScene();
			this.SetScene(scene);
			this.Alignment = (uint)(AlignmentFlag.AlignLeft | AlignmentFlag.AlignTop);
			this.MouseTracking = true;
			
			// Cleanup any previous.
			this.dragRect = null;
			this.hoverRect = null;
			this.mouseDown = null;
			this.tilemap = null;
			this.Spritesheet = null;
		}
		
		public void SetTilemap(string filename)
		{
			this.Reset();
			
			// Get spritesheet for height/width purposes
			Spritesheet = Map.Spritesheets[Map.Spritesheets.IndexOf(filename)];
			((TilePainterTool)this.MapDocument.ToolManager.Tools[ToolTypes.TilePainter]).CurrentSpritesheet = Spritesheet;
			
			// Get Pixmap
			tilemap = new QGraphicsPixmapItem(new QPixmap(filename));
			tilemap.SetZValue(1);
			scene.AddItem(tilemap);
		}
		
		protected override void MousePressEvent(QMouseEvent arg1)
		{
			if (tilemap == null)
				return;
			base.MousePressEvent (arg1);
			
			// Left mouse button raised this event?
			if (arg1.Button() == Qt.MouseButton.LeftButton)
			{
				mouseDown = this.MapToScene(arg1.Pos()).ToPoint();
				
				// Check we're not outside spritesheet bounds!
				if (mouseDown.X() < 0)
					mouseDown.SetX(0);
				if (mouseDown.Y() < 0)
					mouseDown.SetY(0);
				if (mouseDown.X() > Spritesheet.Width - GridSize)
					mouseDown.SetX(Spritesheet.Width - GridSize);
				if (mouseDown.Y() > Spritesheet.Height - GridSize)
					mouseDown.SetY(Spritesheet.Height - GridSize);
			}
		}
		
		protected override void MouseMoveEvent(QMouseEvent arg1)
		{
			if (tilemap == null)
				return;
			base.MouseMoveEvent (arg1);
			
			// Mouse position
			QPoint mouse = this.MapToScene(arg1.Pos()).ToPoint();
			
			// Check we're not outside spritesheet bounds!
			// Need to handle the width/height bounds depending on if we're dragging!
			if (mouse.X() < 0)
				mouse.SetX(0);
			if (mouse.Y() < 0)
				mouse.SetY(0);
			
			// Left mouse button down?
			if ((arg1.Buttons() & (uint)MouseButton.LeftButton) > 0)
			{
				// Hide the hover!
				if (hoverRect != null)
					hoverRect.SetVisible(false);
			
				// If we're dragging, we want to limit AT the width/height (not minus gridsize)
				if (mouse.X() > Spritesheet.Width)
					mouse.SetX(Spritesheet.Width);
				if (mouse.Y() > Spritesheet.Height)
					mouse.SetY(Spritesheet.Height);
			
				// int division.. i think ;)
				// if this ever stops working, don't try to figure it out, just rewrite
				double x = (mouseDown.X() / GridSize); // + ((mouseDown.X() % GridSize) > 0 ? 1 : 0); -- didnt feel right
                double y = (mouseDown.Y() / GridSize); // + ((mouseDown.X() % GridSize) > 0 ? 1 : 0);
                double w = (mouse.X() / GridSize) + ((mouse.X() % GridSize) > 0 ? 1 : 0) - x; // maybe add one so we 'round up'
                double h = (mouse.Y() / GridSize) + ((mouse.Y() % GridSize) > 0 ? 1 : 0) - y; 
                
                // Negative dragging!
               	if (w < 0)
               	{
               		x += w;
               		w = Math.Abs(w);
               	}
               	if (h < 0)
               	{
               		y += h;
               		h = Math.Abs(h);
               	}
                
                this.makeSelection((int)x, (int)y, (int)w, (int)h);
			} 
			else
			{
				// If we're not dragging, we wanna limit at the last tile... minus gridsize!
				if (mouse.X() > Spritesheet.Width - GridSize)
					mouse.SetX(Spritesheet.Width - GridSize);
				if (mouse.Y() > Spritesheet.Height - GridSize)
					mouse.SetY(Spritesheet.Height - GridSize);
			
				int x = (mouse.X() / GridSize); // integer division ;)
				int y = (mouse.Y() / GridSize);
				hoverPosition = new Point(x, y); // wants tile co-ords
				x *= GridSize;
				y *= GridSize;
				
				if (hoverRect == null)
				{
					hoverRect = new QGraphicsRectItem(x, y, GridSize, GridSize);
					hoverRect.SetPen(new QPen(new QColor(255, 0, 255)));
					hoverRect.SetZValue(3);
					this.scene.AddItem(hoverRect);
				}
				else
				{
					hoverRect.SetRect(x, y, GridSize, GridSize);
					hoverRect.SetVisible(true);
				}
			}
			
		}
		
		protected override void MouseReleaseEvent(QMouseEvent arg1)
		{
			if (tilemap == null)
				return;
			
			// If the hoverRect is still visible, then we never dragged and the user has clicked a single tile.
			if (hoverRect != null && hoverRect.IsVisible())
			{
				makeSelection(hoverPosition.X, hoverPosition.Y, 1, 1);
				hoverRect.SetVisible(false);
			}
		}
		
		private void makeSelection(int x, int y, int w, int h)
		{
			// Remember tile co-ords of selection
            ((TilePainterTool)this.MapDocument.ToolManager.Tools[ToolTypes.TilePainter]).SpritesheetSelection = new Rectangle(x, y, w, h);
            
            // convert form tile co-ords -> position co-ords
            x *= GridSize;
            y *= GridSize;
            w *= GridSize;
            h *= GridSize;
            
            // Draw Rect
            if (dragRect == null)
            {
            	dragRect = new QGraphicsRectItem(x, y, w, h);
            	dragRect.SetZValue(2);
            	this.scene.AddItem(dragRect);
            }
            else
            	dragRect.SetRect(x, y, w, h);
		}
	}
}

